package zpe.controledeacesso.jsf.view.bean;

import static zpe.genericcrud.jsf.view.bean.FacesUtils.addErrorMessage;
import static zpe.genericcrud.jsf.view.bean.FacesUtils.addInfoMessage;

import java.util.ArrayList;
import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;

import org.springframework.util.CollectionUtils;

import zpe.controledeacesso.model.bean.GrupoDeUsuario;
import zpe.controledeacesso.model.bean.Modulo;
import zpe.controledeacesso.model.bean.NivelAcesso;
import zpe.controledeacesso.model.bean.Permissoes;
import zpe.controledeacesso.model.bean.Transacao;
import zpe.controledeacesso.model.bean.Usuario;
import zpe.controledeacesso.security.Acesso;
import zpe.controledeacesso.service.GrupoDeUsuariosService;
import zpe.controledeacesso.util.UsuarioUtil;
import zpe.genericcrud.exception.ApplicationException;
import zpe.genericcrud.jsf.view.bean.AbstractCrudBean;
import zpe.genericcrud.jsf.view.bean.FacesUtils;

@SuppressWarnings("rawtypes")

@ManagedBean(name="grupoDeUsuariosBean")
@ViewScoped
public class GrupoDeUsuariosBean extends AbstractCrudBean<GrupoDeUsuario, GrupoDeUsuariosService> {

	private DataModel resultPossiveis  = new ListDataModel();
	private DataModel resultExistentes  = new ListDataModel();
	private List<SelectItem> niveis = new ArrayList<SelectItem>();
	private List<Transacao> transacoesPossiveis = new ArrayList<Transacao>(0); 
	
	@ManagedProperty("#{grupoDeUsuariosService}")
	protected GrupoDeUsuariosService service;

	@Override
	public void setService(GrupoDeUsuariosService service) {
		super.setService(service);
	}
	
	@Override
	public void cancel(ActionEvent event) {
		resultPossiveis = new ListDataModel();
		resultExistentes  = new ListDataModel();
		super.cancel(event);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void prepareInsert(ActionEvent event) {
		super.prepareInsert(event);
		Modulo mod = new Modulo();
		mod.setId( Acesso.getModuloSistema() );
		getEntity().setModulo( mod );
		populaPossiveis();
		
		List<Transacao> transacaos = (List<Transacao>) getResultPossiveis().getWrappedData();
		
		if(!CollectionUtils.isEmpty(transacaos)) {
			transacoesPossiveis.addAll(transacaos);
		}
	}

	/**
	 * Preenche a lista de transações possíveis para o módulo e para o usuário logado. 
	 * Se tiver no estado de edição, retira da lista de transações possíveis as que já estão no grupo.
	 */
	public void populaPossiveis() {
		
		if( getResultPossiveis().getRowCount() <= 0 ){
			// resultados possíveis
			List<Transacao> transacoes = getService().getServiceTransacao().findByModulo(getEntity().getModulo().getId());
		
			Usuario usuario = UsuarioUtil.getUsuarioLogado();				
			NivelAcesso nivelSuperior = usuario.getMaiorNivel( getEntity().getModulo().getId() );
			// eliminando, da lista de possíveis, as transações que já existem no grupo.
			for (int i = 0; i < getEntity().getGrupoTransacao().size(); i++) {
				for (int j = 0; j < transacoes.size(); j++) {
					if( !nivelSuperior.equals( NivelAcesso.MASTER ) ){
						if( transacoes.get(j).getUsedLabel().equals( "grupoUsuarios" ) ){
							transacoes.remove(j);
							continue;
						}
					}
					if (getEntity().getGrupoTransacao().get(i).getTransacao().getId().intValue() == transacoes.get(j).getId().intValue())
						transacoes.remove(j);
				}
			}
	
			getResultPossiveis().setWrappedData(transacoes);		
		}
		preencheNiveis();
	}
	
	public void populaExistentes() {
		List<Permissoes> lista = getService().getServicePermissoes().consultaPermissoesByGrupo( getEntity().getId() );
		List<Permissoes> listaAux = new ArrayList<Permissoes>();
		for (Permissoes p : lista) {
			if ((p.getTransacao().getModulos().contains(getEntity().getModulo())) && !(listaAux.contains(p)))
				listaAux.add(p);
		}

		getEntity().setGrupoTransacao(listaAux);
		getResultExistentes().setWrappedData(listaAux);
	}

	/**
	 * Preenche os níveis de acesso permitidos para o usuário conceder.
	 */
	private void preencheNiveis(){
		int mod = Acesso.getModuloSistema();		
		Usuario usuario = UsuarioUtil.getUsuarioLogado();				
		NivelAcesso nivelSuperior = usuario.getMaiorNivel(mod);
		setNiveis(new ArrayList<SelectItem>());
		for( int i = 1; i < nivelSuperior.ordinal(); i++ ){
			SelectItem si = new SelectItem();
			si.setLabel( i + " - " + NivelAcesso.values()[i]);
			si.setValue( NivelAcesso.values()[i] );
			getNiveis().add( si );
		}
		if( nivelSuperior.equals( NivelAcesso.MASTER ) ){
			getNiveis().add( new SelectItem( nivelSuperior, nivelSuperior.ordinal() + " - " + nivelSuperior, null, true ) );
		}
	}
	
	@SuppressWarnings("unchecked")
	public void findByNivel(ActionEvent event) {
		try {
			// pegando o usuário logado
			Usuario usuario = UsuarioUtil.getUsuarioLogado();		
			int modulo = Acesso.getModuloSistema();
			NivelAcesso nivelDeAcesso = usuario.getMaiorNivel( modulo );
			List<GrupoDeUsuario> listAux = null;

			getEntitySearch().getModulo().setId(modulo);
			getEntitySearch().setNivelDeAcesso(nivelDeAcesso);

			listAux = getService().findByNivel( getEntitySearch() );
			
			if(listAux.isEmpty()) {
				FacesUtils.addWarnMessage("Nenhum grupo de usuários foi encontrado.");
			}
			
			getResultSearch().setWrappedData(listAux);
			setOriginalResult((List<GrupoDeUsuario>) getResultSearch().getWrappedData());
			setCurrentState(STATE_SEARCH);		
		} catch (ApplicationException e) {
			new ApplicationException("Ocorreu um erro durante a consulta.");
		}
	}

	@Override
	public void prepareUpdate(ActionEvent event) {
		Modulo mod = new Modulo();
		mod.setId( Acesso.getModuloSistema() );
		getEntity().setModulo( mod );
		super.prepareUpdate(event);
		populaExistentes();
		populaPossiveis();
	} 

	/**
	 * Adiciona uma permissão às transações existentes e retira das transações possíveis.
	 * @param ev
	 */
	@SuppressWarnings("unchecked")
	public void selecionarPossivel(ActionEvent ev) {
		Transacao t = (Transacao) getResultPossiveis().getRowData();
		List<Permissoes> permissoes = (List<Permissoes>) getResultExistentes().getWrappedData();

		if( permissoes == null ){
			permissoes = new ArrayList<Permissoes>();
		}
		
		Permissoes p = new Permissoes('N', 'N', 'N');
		p.setGrupoDeUsuario(getEntity());
		p.setTransacao(t);

		permissoes.add(p);

		List<Transacao> transacoes = (List<Transacao>) getResultPossiveis().getWrappedData();
		transacoes.remove(t);

		getResultPossiveis().setWrappedData(transacoes);
		
		getResultExistentes().setWrappedData(permissoes);
	}

	/**
	 * Retira uma permissão das transações existentes e adiciona às transações possíveis.
	 * @param ev
	 */
	@SuppressWarnings("unchecked")
	public void selecionarExistente(ActionEvent ev) {
		Permissoes p = (Permissoes) getResultExistentes().getRowData();

		// adicionando na lista das transações existentes
		List<Permissoes> permissoes = (List<Permissoes>) getResultExistentes().getWrappedData();
		
		for (int i = 0; i < permissoes.size(); i++) {
			if (permissoes.get(i).getTransacao().getId() == p.getTransacao().getId())
				permissoes.remove(i);
		}
		
		// adicionando na lista das transações possíveis
		List<Transacao> transacoes = (List<Transacao>) getResultPossiveis().getWrappedData();
		
		if( transacoes == null ){
			transacoes = new ArrayList<Transacao>();
		}
		
		transacoes.add(p.getTransacao());
		
		getResultExistentes().setWrappedData( permissoes );
		getResultPossiveis().setWrappedData( transacoes );
	}

	@SuppressWarnings("unchecked")
	@Override
	public void save(ActionEvent event) {
		try{
			
			List<Permissoes> permissoes = (List<Permissoes>) getResultExistentes().getWrappedData();
			
			if(permissoes == null || permissoes.isEmpty()) {
				throw new ApplicationException("É obrigatório informar pelo menos uma transação para o grupo.");
			}
			
			Modulo mod = new Modulo();
			mod.setId( Acesso.getModuloSistema() );
			getEntity().setModulo( mod );
			
			getService().insert( getEntity(), permissoes );
			addInfoMessage( getMessage( "SUCCESS_INSERT" ) );
			setCurrentState( STATE_INSERT);
			setEntity( getNewEntityInstance() );
			
			getResultExistentes().setWrappedData(null);
			
			getResultPossiveis().setWrappedData(transacoesPossiveis);
			
		} catch ( ApplicationException e ) {
			addErrorMessage( e.getMessage() );
		} catch ( Exception e ) {
			addErrorMessage( getMessage( "ERROR_INSERT" ) );
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void update(ActionEvent event) {
		try{
			
			List<Permissoes> permissoes = (List<Permissoes>) getResultExistentes().getWrappedData();
			
			if(permissoes == null || permissoes.isEmpty()) {
				throw new ApplicationException("É obrigatório informar pelo menos uma transação para o grupo.");
			}
			
			getService().update( getEntity(), permissoes );
			addInfoMessage( getMessage( "SUCCESS_UPDATE" ) );
			setCurrentState( STATE_SEARCH );
			
			getResultExistentes().setWrappedData(null);
			getResultPossiveis().setWrappedData(null);
			
		} catch (ApplicationException e) {
			addErrorMessage(e.getMessage());
		} catch (Exception e) {
			addErrorMessage(getMessage("ERROR_UPDATE"));
		}
	}
	
	/**
	 * @return the resultPossiveis
	 */
	public DataModel getResultPossiveis() {
		return resultPossiveis;
	}

	/**
	 * 
	 * @param resultPossiveis
	 *            the resultPossiveis to set
	 */
	public void setResultPossiveis(DataModel resultPossiveis) {
		this.resultPossiveis = resultPossiveis;
	}

	/**
	 * @return the resultExistentes
	 */
	public DataModel getResultExistentes() {
		return resultExistentes;
	}

	/**
	 * @param resultExistentes
	 *            the resultExistentes to set
	 */
	public void setResultExistentes(DataModel resultExistentes) {
		this.resultExistentes = resultExistentes;
	}

	private List<SelectItem> itens = new ArrayList<SelectItem>();

	public List<SelectItem> getAllSelectItem() {
		if( itens.isEmpty() ){
			return beanToSelectItem() ;
		}else{
			return itens;
		}
	}

	/**
	 * Preenche a lista de grupos que o usuário logado pode ver.
	 * @param entities
	 * @return
	 */
	protected List<SelectItem> beanToSelectItem() {

		int modulo = Acesso.getModuloSistema();
		NivelAcesso maiorNivel = UsuarioUtil.getUsuarioLogado().getMaiorNivel( modulo );
		List<GrupoDeUsuario> grupos = getService().findByNivel(maiorNivel, modulo);
		for (GrupoDeUsuario e : grupos) {

			//A não ser o Master, todos os outros grupos só podem ver os de níveis mais baixos.
			if ( e.getNivelDeAcesso() != null ) {
				SelectItem si = new SelectItem();
				si.setLabel( e.getDescricao() + " - "+ e.getModulo().getDescricao() );
				si.setValue(e);
				itens.add(si);
			}
		}
		return itens;
	}

	public List<SelectItem> getNiveis() {
		return niveis;
	}

	public void setNiveis(List<SelectItem> niveis) {
		this.niveis = niveis;
	}
}